// Fill out your copyright notice in the Description page of Project Settings.


#include "ProcedrualBarComponent.h"

// Sets default values for this component's properties
UProcedrualBarComponent::UProcedrualBarComponent()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// ...
}


// Called when the game starts
void UProcedrualBarComponent::BeginPlay()
{
	Super::BeginPlay();

	// ...
	
}

void UProcedrualBarComponent::OnRegister()
{
	Super::OnRegister();

	AActor* Owner = this->GetOwner();
	check(Owner);

	PMeshComponent = NewObject<UProceduralMeshComponent>(this, "Mesh");
	PMeshComponent->SetFlags(RF_Transient);
	PMeshComponent->RegisterComponent();
	Owner->AddInstanceComponent(PMeshComponent);
	PMeshComponent->AttachToComponent(this, FAttachmentTransformRules::KeepRelativeTransform);

	LabelNameComponent = NewObject<UTextRenderComponent>(this, "LabelName");
	LabelNameComponent->SetFlags(RF_Transient);
	LabelNameComponent->RegisterComponent();
	Owner->AddInstanceComponent(LabelNameComponent);
	LabelNameComponent->AttachToComponent(this, FAttachmentTransformRules::KeepRelativeTransform);

	LabelValueComponent = NewObject<UTextRenderComponent>(this, "LabelValue");
	LabelValueComponent->SetFlags(RF_Transient);
	LabelValueComponent->RegisterComponent();
	Owner->AddInstanceComponent(LabelValueComponent);
	LabelValueComponent->AttachToComponent(this, FAttachmentTransformRules::KeepRelativeTransform);
}


// Called every frame
void UProcedrualBarComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ...
}

void UProcedrualBarComponent::SetColor(const FLinearColor Color)
{
	if (MaterialInstance)
	{
		MaterialInstance->SetVectorParameterValue(TEXT("Color"), Color);
	}
}

void UProcedrualBarComponent::Rebuild(float Width, float Height, float Bevel, UMaterialInterface* MaterialMesh,
	FString LabelName, FString LabelValue, UMaterialInterface* MaterialLabel)
{
	TArray<FVector> Vertices;
	TArray<int32> Indices;
	TArray<FVector> Normals;
	TArray<FVector2D> UVs;
	TArray<FProcMeshTangent> Tangents;
	TArray<FLinearColor> VertexColors;

	// Bottom Face
	Vertices.Add(FVector(Width - Bevel, Width - Bevel, 0));
	Vertices.Add(FVector(Bevel, Width - Bevel, 0));
	Vertices.Add(FVector(Bevel, Bevel, 0));
	Vertices.Add(FVector(Width - Bevel, Bevel, 0));	

	for (int i = 0; i < 4; ++i)
	{
		Normals.Add(-FVector::UnitZ());
		UVs.Add(FVector2D::ZeroVector);
		Tangents.Add(FProcMeshTangent(0, 1, 0));
		VertexColors.Add(FLinearColor::White);
	}

	// Top Face
	Vertices.Add(FVector(Bevel, Width - Bevel, Height));
	Vertices.Add(FVector(Width - Bevel, Width - Bevel, Height));
	Vertices.Add(FVector(Width - Bevel, Bevel, Height));
	Vertices.Add(FVector(Bevel, Bevel, Height));
	
	for (int i = 0; i < 4; ++i)
	{
		Normals.Add(FVector::UnitZ());
		UVs.Add(FVector2D::ZeroVector);
		Tangents.Add(FProcMeshTangent(0, 1, 0));
		VertexColors.Add(FLinearColor::White);
	}

	// Right Face
	Vertices.Add(FVector(Width - Bevel, Width, Height -  Bevel));
	Vertices.Add(FVector(Bevel, Width, Height - Bevel));
	Vertices.Add(FVector(Bevel, Width, Bevel));
	Vertices.Add(FVector(Width - Bevel, Width, Bevel));
	
	for (int i = 0; i < 4; ++i)
	{
		Normals.Add(FVector::UnitY());
		UVs.Add(FVector2D::ZeroVector);
		Tangents.Add(FProcMeshTangent(0, 1, 0));
		VertexColors.Add(FLinearColor::White);
	}

	// Left Face
	Vertices.Add(FVector(Bevel, 0, Height - Bevel));
	Vertices.Add(FVector(Width - Bevel, 0, Height - Bevel));
	Vertices.Add(FVector(Width - Bevel, 0, Bevel));
	Vertices.Add(FVector(Bevel, Bevel, 0));

	for (int i = 0; i < 4; ++i)
	{
		Normals.Add(-FVector::UnitY());
		UVs.Add(FVector2D::ZeroVector);
		Tangents.Add(FProcMeshTangent(0, 1, 0));
		VertexColors.Add(FLinearColor::White);
	}

	// Front Face
	Vertices.Add(FVector(Width, Bevel, Height - Bevel));
	Vertices.Add(FVector(Width, Width - Bevel, Height - Bevel));
	Vertices.Add(FVector(Width, Width - Bevel, Bevel));
	Vertices.Add(FVector(Width, Bevel, Bevel));

	for (int i = 0; i < 4; ++i)
	{
		Normals.Add(FVector::UnitX());
		UVs.Add(FVector2D::ZeroVector);
		Tangents.Add(FProcMeshTangent(0, 1, 0));
		VertexColors.Add(FLinearColor::White);
	}

	// Back Face
	Vertices.Add(FVector(0, Width - Bevel, Height - Bevel));
	Vertices.Add(FVector(0, Bevel, Height - Bevel));
	Vertices.Add(FVector(0, Bevel, Bevel));
	Vertices.Add(FVector(0, Width - Bevel, Bevel));

	for (int i = 0; i < 4; ++i)
	{
		Normals.Add(-FVector::UnitX());
		UVs.Add(FVector2D::ZeroVector);
		Tangents.Add(FProcMeshTangent(0, 1, 0));
		VertexColors.Add(FLinearColor::White);
	}

	for (int i = 0; i < 6; ++i)
	{
		Indices.Add(i * 4); Indices.Add(i * 4 + 1); Indices.Add(i * 4 + 2);
		Indices.Add(i * 4); Indices.Add(i * 4 + 2); Indices.Add(i * 4 + 3);
	}

	PMeshComponent->CreateMeshSection_LinearColor(0, Vertices, Indices, Normals, UVs, VertexColors, Tangents, false);

	auto BuildBevelFaces = [this](float width, float height, float bevel)
	{
		TArray<FVector> Vertices;
		TArray<int32> Indices;
		TArray<FVector> Normals;
		TArray<FVector2D> UVs;
		TArray<FProcMeshTangent> Tangents;
		TArray<FLinearColor> VertexColors;

		// TopBack Face
		Vertices.Add(FVector(bevel, width - bevel, height));
		Vertices.Add(FVector(bevel, bevel, height));
		Vertices.Add(FVector(0, bevel, height - bevel));
		Vertices.Add(FVector(0,  width - bevel, height - bevel));

		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((FVector::UnitZ() - FVector::UnitX()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// TopFront Face
		Vertices.Add(FVector(width - bevel, bevel, height));
		Vertices.Add(FVector(width - bevel, width - bevel, height));
		Vertices.Add(FVector(width, width - bevel, height - bevel));
		Vertices.Add(FVector(width,  bevel, height - bevel));

		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((FVector::UnitZ() + FVector::UnitX()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// TopLeft Face
		Vertices.Add(FVector(bevel, bevel, height));
		Vertices.Add(FVector(width - bevel, bevel, height));
		Vertices.Add(FVector(width - bevel, 0, height - bevel));
		Vertices.Add(FVector(bevel,  0, height - bevel));

		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((FVector::UnitZ() - FVector::UnitY()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// TopRight Face
		Vertices.Add(FVector(width - bevel, width - bevel, height));
		Vertices.Add(FVector(bevel, width - bevel, height));
		Vertices.Add(FVector(bevel, width, height - bevel));
		Vertices.Add(FVector(width - bevel,  width, height - bevel));

		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((FVector::UnitZ() + FVector::UnitY()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// BottomBack Face
		Vertices.Add(FVector(0, width - bevel, bevel));
		Vertices.Add(FVector(0, bevel, bevel));
		Vertices.Add(FVector(bevel, bevel, 0));
		Vertices.Add(FVector(bevel,  width - bevel, 0));

		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((-FVector::UnitZ() - FVector::UnitY()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// BottomFront Face
		Vertices.Add(FVector(width, bevel, bevel));
		Vertices.Add(FVector(width, width - bevel, bevel));
		Vertices.Add(FVector(width - bevel, width - bevel, 0));
		Vertices.Add(FVector(width - bevel,  bevel, 0));

		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((-FVector::UnitZ() + FVector::UnitY()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// BottomLeft Face
		Vertices.Add(FVector(bevel, 0, bevel));
		Vertices.Add(FVector(width - bevel, 0, bevel));
		Vertices.Add(FVector(width - bevel, bevel, 0));
		Vertices.Add(FVector(bevel,  bevel, 0));

		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((-FVector::UnitZ() - FVector::UnitX()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// BottomRight Face
		Vertices.Add(FVector(width - bevel, width, bevel));
		Vertices.Add(FVector(bevel, width, bevel));
		Vertices.Add(FVector(bevel, width - bevel, 0));
		Vertices.Add(FVector(width - bevel,  width - bevel, 0));
		
		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((-FVector::UnitZ() + FVector::UnitX()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// LeftBack Face
		Vertices.Add(FVector(0, bevel, height - bevel));
		Vertices.Add(FVector(bevel, 0, height - bevel));
		Vertices.Add(FVector(bevel, 0, bevel));
		Vertices.Add(FVector(0,  bevel, bevel));
		
		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((-FVector::UnitY() - FVector::UnitX()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// RightBack Face
		Vertices.Add(FVector(bevel, width, height - bevel));
		Vertices.Add(FVector(0, width - bevel, height - bevel));
		Vertices.Add(FVector(0, width - bevel, bevel));
		Vertices.Add(FVector(bevel, width, bevel));
		
		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((FVector::UnitY() - FVector::UnitX()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// LeftFront Face
		Vertices.Add(FVector(width - bevel, 0, height - bevel));
		Vertices.Add(FVector(width, bevel, height - bevel));
		Vertices.Add(FVector(width, bevel, bevel));
		Vertices.Add(FVector(width - bevel, 0, bevel));
		
		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((-FVector::UnitY() + FVector::UnitX()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// RightFront Face
		Vertices.Add(FVector(width, width - bevel, height - bevel));
		Vertices.Add(FVector(width - bevel, width, height - bevel));
		Vertices.Add(FVector(width - bevel, width, bevel));
		Vertices.Add(FVector(width, width - bevel, bevel));
		
		for (int i = 0; i < 4; ++i)
		{
			Normals.Add((FVector::UnitY() + FVector::UnitX()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		for (int i = 0; i < 12; ++i)
		{
			Indices.Add(i * 4); Indices.Add(i * 4 + 1); Indices.Add(i * 4 + 2);
			Indices.Add(i * 4); Indices.Add(i * 4 + 2); Indices.Add(i * 4 + 3);
		}

		// TLB Corner Face
		Vertices.Add(FVector(bevel, bevel, height));
		Vertices.Add(FVector(bevel, 0, height - bevel));
		Vertices.Add(FVector(0, bevel, height - bevel));
		for (int i = 0; i < 3; ++i)
		{
			Normals.Add(((-FVector::UnitY() - FVector::UnitX()).GetSafeNormal() + FVector::UnitZ()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// TRB Corner Face
		Vertices.Add(FVector(bevel, width - bevel, height));
		Vertices.Add(FVector(0, width - bevel, height - bevel));
		Vertices.Add(FVector(bevel, width, height - bevel));
		for (int i = 0; i < 3; ++i)
		{
			Normals.Add(((FVector::UnitY() - FVector::UnitX()).GetSafeNormal() + FVector::UnitZ()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// TLF Corner Face
		Vertices.Add(FVector(width - bevel, bevel, height));
		Vertices.Add(FVector(width, bevel, height - bevel));
		Vertices.Add(FVector(width - bevel, 0, height - bevel));
		for (int i = 0; i < 3; ++i)
		{
			Normals.Add(((-FVector::UnitY() + FVector::UnitX()).GetSafeNormal() + FVector::UnitZ()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// TRF Corner Face
		Vertices.Add(FVector(width - bevel, width - bevel, height));
		Vertices.Add(FVector(width - bevel, width, height - bevel));
		Vertices.Add(FVector(width, width - bevel, height - bevel));
		for (int i = 0; i < 3; ++i)
		{
			Normals.Add(((FVector::UnitY() + FVector::UnitX()).GetSafeNormal() + FVector::UnitZ()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// BLB Corner Face
		Vertices.Add(FVector(0, bevel, bevel));
		Vertices.Add(FVector(bevel, 0, bevel));
		Vertices.Add(FVector(bevel, bevel, 0));
		for (int i = 0; i < 3; ++i)
		{
			Normals.Add(((-FVector::UnitY() - FVector::UnitX()).GetSafeNormal() - FVector::UnitZ()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// BRB Corner Face
		Vertices.Add(FVector(bevel, width, bevel));
		Vertices.Add(FVector(0, width - bevel, bevel));
		Vertices.Add(FVector(bevel, width - bevel, 0));
		for (int i = 0; i < 3; ++i)
		{
			Normals.Add(((FVector::UnitY() - FVector::UnitX()).GetSafeNormal() - FVector::UnitZ()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// BLF Corner Face
		Vertices.Add(FVector( width - bevel, 0, bevel));
		Vertices.Add(FVector(width, bevel, bevel));
		Vertices.Add(FVector(width - bevel, bevel, 0));
		for (int i = 0; i < 3; ++i)
		{
			Normals.Add(((-FVector::UnitY() + FVector::UnitX()).GetSafeNormal() - FVector::UnitZ()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		// BRF Corner Face
		Vertices.Add(FVector(width, width - bevel, bevel));
		Vertices.Add(FVector(width - bevel, width, bevel));
		Vertices.Add(FVector(width - bevel, width - bevel, 0));
		for (int i = 0; i < 3; ++i)
		{
			Normals.Add(((FVector::UnitY() + FVector::UnitX()).GetSafeNormal() - FVector::UnitZ()).GetSafeNormal());
			UVs.Add(FVector2D::ZeroVector);
			Tangents.Add(FProcMeshTangent(0, 1, 0));
			VertexColors.Add(FLinearColor::White);
		}

		for (int i = 0; i < 8; ++i)
		{
			constexpr int BaseIndex = 48;
			Indices.Add(BaseIndex + i * 3); Indices.Add(BaseIndex + i * 3 + 1); Indices.Add(BaseIndex + i * 3 + 2);
		}

		PMeshComponent->CreateMeshSection_LinearColor(1, Vertices, Indices, Normals, UVs, VertexColors, Tangents, false);
	};

	BuildBevelFaces(Width, Height, Bevel);

	if (MaterialMesh)
	{
		MaterialInstance = UMaterialInstanceDynamic::Create(MaterialMesh, this);
		PMeshComponent->SetMaterial(0, MaterialInstance);
		PMeshComponent->SetMaterial(1, MaterialInstance);
	}

	LabelNameComponent->SetText(FText::FromString(LabelName));
	LabelNameComponent->SetHorizontalAlignment(EHorizTextAligment::EHTA_Center);
	LabelNameComponent->SetVerticalAlignment(EVerticalTextAligment::EVRTA_TextCenter);
	LabelNameComponent->SetWorldSize(24);
	LabelNameComponent->SetCastShadow(false);
	LabelNameComponent->SetRelativeLocation(FVector(Width / 2.0f, Width / 2.0f, Height + 20.0f));
	LabelNameComponent->SetRelativeRotation(FRotator(0, 180.0f, 0));
	LabelNameComponent->SetMaterial(0, MaterialLabel);
	LabelNameComponent->SetTextRenderColor(FColor::Orange);

	LabelValueComponent->SetText(FText::FromString(LabelValue));
	LabelValueComponent->SetHorizontalAlignment(EHorizTextAligment::EHTA_Center);
	LabelValueComponent->SetVerticalAlignment(EVerticalTextAligment::EVRTA_TextCenter);
	LabelValueComponent->SetWorldSize(24);
	LabelValueComponent->SetCastShadow(false);
	LabelValueComponent->SetRelativeLocation(FVector(-1.0f, Width / 2.0f, Height - 20.0f));
	LabelValueComponent->SetRelativeRotation(FRotator(0, 180.0f, 0));
	LabelValueComponent->SetMaterial(0, MaterialLabel);
}
